package com.pactera.madp.cp.common.util.sync;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.pactera.madp.cp.api.entity.sync.SyncStateEntity;
import com.pactera.madp.cp.common.constant.Constants;
import com.pactera.madp.cp.context.SyncStateContext;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;

@Slf4j
@UtilityClass
public class SyncUtils {

    /**
     * 属性映射
     * @param t
     * @param values
     * @param <T>
     * @return
     */
    public <T> T copyProperties(T t, String[] values) {
        if (t == null) {
            throw new IllegalArgumentException("拷贝对象不能为空");
        }
        Class<?> clazz = t.getClass();
        Field[] fields = clazz.getDeclaredFields();
        if (fields.length == 0 || values == null || values.length == 0) {
            return null;
        }

        // length - 4 排除非文件字段
        int len = fields.length - 4 < values.length ? fields.length - 4 : values.length;

        doCopyProperties(t, values, fields, len);

        return t;
    }

    /**
     * 属性映射,因crm系统有不规范的数据，所以特殊处理
     * @param t
     * @param values
     * @param <T>
     * @return
     */
    public <T> T copyCrmProperties(T t, String[] values) {
        if (t == null) {
            throw new IllegalArgumentException("拷贝对象不能为空");
        }
        Class<?> clazz = t.getClass();
        Field[] fields = clazz.getDeclaredFields();
        if (fields.length == 0 || values == null || values.length == 0) {
            return null;
        }

        // length - 4 排除非文件字段
        if (fields.length - 4 < values.length) {
            return null;
        }
        int len = fields.length - 4 > values.length ? values.length : fields.length - 4;

        doCopyProperties(t, values, fields, len);

        return t;
    }



    /**
     * 校验文件是否生成完毕
     * @param filepath
     * @return
     */
    public boolean checkFile(String filepath) {
        if (StrUtil.isEmpty(filepath)) {
            throw new IllegalArgumentException("文件路径不能为空");
        }
        File file = new File(filepath);
        return file.exists();
    }

    /**
     * 校验目录是否存在
     * @param dir
     * @param isCreate
     * @return
     */
    public boolean checkDir(String dir, boolean isCreate) {
        if (StrUtil.isEmpty(dir)) {
            throw new IllegalArgumentException("文件路径不能为空");
        }
        File file = new File(dir);
        if (isCreate) {
            if (!file.exists()) {
                return file.mkdirs();
            }
            return true;
        }
        return file.exists();
    }

    /**
     * 文件解析
     * @param file
     * @param separator
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> List<T> parse(File file, String separator, Class<T> clazz, String charset) {
        if (ObjectUtil.isNull(file)) {
            throw new IllegalArgumentException("文件不能为空");
        }
        FileReader fileReader = new FileReader(file, charset);
        List<String> lines = fileReader.readLines();
        return doParse(lines, separator, clazz);
    }

    /**
     * 数据解析
     * @param lines
     * @param separator
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> List<T> parse(List<String> lines, String separator, Class<T> clazz) {
        if (ObjectUtil.isNull(lines)) {
            throw new IllegalArgumentException("数据列表不能为空");
        }
        return doParse(lines, separator, clazz);
    }

    /**
     * 数据解析,因crm系统有不规范的数据，所以特殊处理
     * @param lines
     * @param separator
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> List<T> crmParse(List<String> lines, String separator, Class<T> clazz) {
        if (ObjectUtil.isNull(lines)) {
            throw new IllegalArgumentException("数据列表不能为空");
        }
        return doCrmParse(lines, separator, clazz);
    }

    /**
     * 分段文件读取
     * @param file
     * @param charset
     * @param n         忽略字节数
     * @param perCount  每次读取数据条数
     * @param lines
     * @return
     */
    @SneakyThrows
    public List segmentReadFile(File file, String charset, long n, int fillChar, int perCount, List<String> lines) {
        if (ObjectUtil.isNull(file)) {
            throw new IllegalArgumentException("文件不能为空");
        }
        BufferedReader reader = null;
        String line;
        try {
            reader = FileUtil.getReader(file, charset);
            reader.skip(n);

            while((line = reader.readLine()) != null) {
                n += line.length() + fillChar;
                if (!StrUtil.isEmpty(line)) {
                    lines.add(line);
                }
                if (lines.size() == perCount) {
                    break;
                }
            }
        } finally {
            if (reader != null) {
                reader.close();
            }
        }

        List result = new ArrayList(2);
        if (line == null) {
            result.add(false);
        } else {
            result.add(true);
        }
        result.add(n);
        return result;
    }

    /**
     * 获取最新文件
     * @param filePath
     * @return
     */
    public File getNewestFile(String filePath) {
        if (StrUtil.isEmpty(filePath)) {
            throw new IllegalArgumentException("文件路径不能为空");
        }
        File file = new File(filePath);
        if (!file.exists()) {
            return null;
        }
        if (!file.isDirectory()) {
            return file;
        }

        File[] newestFile = new File[1];
        file.listFiles(f -> {
            if (ObjectUtil.isNotNull(newestFile[0])) {
                newestFile[0] = newestFile[0].lastModified() > f.lastModified() ? newestFile[0] : f;
            } else {
                newestFile[0] = f;
            }
            return false;
        });
        return newestFile[0];
    }

    /**
     * 获取最近历史文件
     * @param hisFilePath
     * @return
     */
    public File getHisFile (String hisFilePath) {
        if (StrUtil.isEmpty(hisFilePath)) {
            throw new IllegalArgumentException("备份文件路径不存在");
        }
        File file = new File(hisFilePath);
        if (!file.isDirectory()) {
            return file;
        }
        // 查询日志表，判断文件是否已被备份过
        SyncStateEntity entity = SyncStateContext.get();
        int count = entity.selectCount(Wrappers.<SyncStateEntity>lambdaQuery()
                .eq(SyncStateEntity::getSyncType, entity.getSyncType())
                .eq(SyncStateEntity::getSyncSubType, entity.getSyncSubType())
                .eq(SyncStateEntity::getSyncDate, entity.getSyncDate())
                .gt(SyncStateEntity::getSyncState, 3));
        if (count < 1) {
            // 未备份过
            throw new RuntimeException("同步数据文件不存在");
        }
        // 已备份过
        File bakFile = getNewestFile(hisFilePath);
        if (ObjectUtil.isNull(bakFile)) {
            // 备份失败
            throw new RuntimeException("备份文件目录为空");
        }
        String bakDate = DateUtil.date(bakFile.lastModified()).toString(Constants.DATE_FORMAT);
        String nowDate = DateUtil.format(new Date(), Constants.DATE_FORMAT);
        if (StrUtil.equals(bakDate, nowDate)) {
            return bakFile;
        }
        // 备份失败
        throw new RuntimeException("备份文件不存在");
    }

    /**
     * 备份并删除源文件
     * @param syncFile
     * @param bakFilePath
     * @return
     */
    public boolean bakAndDelFile(File syncFile, String bakFilePath) {
        if (ObjectUtil.isNull(syncFile)) {
            log.debug("同步文件{}不能为空", syncFile);
            return false;
        }
        if (StrUtil.isEmpty(bakFilePath)) {
            log.debug("备份文件{}不能为空", bakFilePath);
            return false;
        }
        if (!syncFile.exists()) {
            log.debug("同步文件{}不存在", syncFile);
            return false;
        }
        //备份
        FileUtil.copy(syncFile, new File(bakFilePath), true);
        FileUtil.del(syncFile);
        return true;
    }

    /**
     * 构建文件全路径
     * @param dirTemplate
     * @param fileNameTemplate
     * @return
     */
    public String buildFilePath(String dirTemplate, String fileNameTemplate, String fileDate) {
        if (StrUtil.isEmpty(fileDate)) {
            fileDate = DateUtil.format(new Date(), Constants.DATE_FORMAT);
        }
        String dir = dirTemplate.replace("YYYY", fileDate.substring(0,4))
                .replace("MM", fileDate.substring(4,6))
                .replace("DD", fileDate.substring(6,8));
        String name = fileNameTemplate.replace(Constants.DATE_FORMAT, fileDate);
        String filepath = StrUtil.format("{}{}{}", dir, File.separator, name);
        filepath = filepath.replaceAll("\\\\", "/");
        return filepath;
    }

    /**
     * 构建文件全路径
     * @param dirTemplate
     * @return
     */
    public String buildFolderPath(String dirTemplate, String fileDate) {
        if (StrUtil.isEmpty(fileDate)) {
            fileDate = DateUtil.format(new Date(), Constants.DATE_FORMAT);
        }
        String dir = dirTemplate.replace("YYYY", fileDate.substring(0,4))
                .replace("MM", fileDate.substring(4,6))
                .replace("DD", fileDate.substring(6,8));
        String filepath = StrUtil.format("{}{}", dir, File.separator);
        filepath = filepath.replaceAll("\\\\", "/");
        return filepath;
    }

    /**
     * 构建概要文件全路径
     * @param dirTemplate
     * @param fileNameTemplate
     * @return
     */
    public String buildFilePath(String dirTemplate, String fileNameTemplate) {
        String filepath = StrUtil.format("{}{}{}", dirTemplate, File.separator, fileNameTemplate);
        filepath = filepath.replaceAll("\\\\", "/");
        return filepath;
    }

    /**
     * 获取分表表名
     * @param tablePrefix 表名前缀
     * @param key   hash计算key
     * @param tableCount  分表数
     * @return
     */
    public String getTableName(Object key, String tablePrefix, int tableCount) {
        if (StrUtil.isEmpty(tablePrefix)) {
            throw new IllegalArgumentException("表前缀名不能为空");
        }
        if (ObjectUtil.isNull(key)) {
            throw new IllegalArgumentException("key不能为空");
        }
        return tablePrefix + Math.abs(key.hashCode()) % tableCount;
    }

    /**
     * 获取分表集合
     * @param tablePrefix
     * @param tableCount
     * @return
     */
    public List<String> getTableNames(String tablePrefix, int tableCount) {
        if (StrUtil.isEmpty(tablePrefix)) {
            throw new IllegalArgumentException("表前缀名不能为空");
        }
        List<String> tables = new ArrayList<>(tableCount);

        int temp = tableCount;
        while (!(temp % tableCount == 0 && temp != tableCount)) {
            String tableName = tablePrefix + (temp % tableCount);
            tables.add(tableName);
            temp++;
        }
        return tables;
    }

    // 解析数据到实体类
    private  <T> List<T> doParse(List<String> lines, String separator, Class<T> clazz) {
        List<T> entityList = new ArrayList<>();

        lines.forEach(item -> {
            try {
                if (StrUtil.isEmpty(item)) {
                    return;
                }
                String[] values = item.split(separator);
                T t = clazz.newInstance();
                t = copyProperties(t, values);
                if (t == null) {
                    log.error("line details：{}", item);
                    return;
                }
                entityList.add(t);
            } catch (InstantiationException e) {
                log.error(e.getMessage(), e);
            } catch (IllegalAccessException e) {
                log.error(e.getMessage(), e);
            }
        });
        return entityList;
    }

    // crm解析数据到实体类,因crm系统有不规范的数据，所以特殊处理
    private  <T> List<T> doCrmParse(List<String> lines, String separator, Class<T> clazz) {
        List<T> entityList = new ArrayList<>();

        String[] exclueStr = new String[]{"\\\\r","\\\\t","\\\\b","\\\\n","\\\\"};

        lines.forEach(item -> {
            try {
                if (StrUtil.isEmpty(item)) {
                    return;
                }
                // 去除特殊字符
                for (String es : exclueStr) {
                    item = item.replace(es, "");
                }
                String[] values = item.split(separator);
                T t = clazz.newInstance();
                t = copyCrmProperties(t, values);
                if (t == null) {
                    log.error("line details：{}", item);
                    return;
                }
                entityList.add(t);
            } catch (InstantiationException e) {
                log.error(e.getMessage(), e);
            } catch (IllegalAccessException e) {
                log.error(e.getMessage(), e);
            }
        });
        return entityList;
    }

    // 拷贝
    private  <T> void doCopyProperties(T t, String[] values, Field[] fields, int len) {
        Integer valueIndex = null;
        String value = null;

        try {
            for (int i=0; i<len; i++) {
                valueIndex = i;
                value = values[i];

                fields[i].setAccessible(true);
                if (fields[i].getType().isAssignableFrom(BigDecimal.class)) {
                    fields[i].set(t, StrUtil.isNotEmpty(values[i].trim()) ? new BigDecimal(values[i]) : null);
                }
                else if (fields[i].getType().isAssignableFrom(Integer.class)) {
                    fields[i].set(t, StrUtil.isNotEmpty(values[i].trim()) ? Integer.valueOf(values[i]) : null);
                }
                else if (fields[i].getType().isAssignableFrom(String.class)) {
                    fields[i].set(t, values[i].trim());
                }
                else {
                    fields[i].set(t, values[i]);
                }
            }
        } catch (Exception e) {
            log.error("line：index_{}, value_{}", valueIndex, value);
            throw new RuntimeException(e);
        }
    }
}
